
#include "include/CHttpService.h"
#include "include/CGetHostByName.h"
BASENET_BEGIN_NAMESPACE

#define LOCK_STATUS QMutexLocker locker(&m_Mutex)


// http消息回调
static void httpMsgFn(struct mg_connection *c, int ev, void *ev_data, void *fn_data)
{
	CHttpService *pThis = (CHttpService*)fn_data;
	if(pThis)
		pThis->__httpServiceCB(c,ev,ev_data);
}

CHttpService::CHttpService(string url, QObject *p)
    :QThread(p)
{
	m_webBack = nullptr;
	memset(&m_sOpts, 0x00, sizeof(struct mg_tls_opts));
	m_sUrl = url;
	mg_mgr_init(&m_sMgr);
	m_bRunStatus = false;

	m_uploadFileUrl = "";
	m_uploadFileSaveDir = "";
	m_uploadFileBack = nullptr;
	CGetHostByName::initMongoose();
}

CHttpService::CHttpService(string url, long IOsize, long mgRecvBufSize)
{
	m_webBack = nullptr;
	memset(&m_sOpts, 0x00, sizeof(struct mg_tls_opts));
	m_sUrl = url;
	mg_mgr_init(&m_sMgr);
	m_sMgr.mg_ioSize = IOsize;
	m_sMgr.mg_recvbufSize = mgRecvBufSize;
	m_bRunStatus = false;

	m_uploadFileUrl = "";
	m_uploadFileSaveDir = "";
	m_uploadFileBack = nullptr;
	CGetHostByName::initMongoose();
}

CHttpService::CHttpService(string url,struct mg_tls_opts *opts)
{
	m_webBack = nullptr;
	memcpy(&m_sOpts, opts, sizeof(struct mg_tls_opts));
	m_sUrl = url;
	mg_mgr_init(&m_sMgr);
	m_bRunStatus = false;

	m_uploadFileUrl = "";
	m_uploadFileSaveDir = "";
	m_uploadFileBack = nullptr;
	CGetHostByName::initMongoose();
}


CHttpService::CHttpService(string url,struct mg_tls_opts *opts, long IOsize, long mgRecvBufSize)
{
	m_webBack = nullptr;
	memcpy(&m_sOpts, opts, sizeof(struct mg_tls_opts));
	m_sUrl = url;
	mg_mgr_init(&m_sMgr);
	m_sMgr.mg_ioSize = IOsize;
	m_sMgr.mg_recvbufSize = mgRecvBufSize;
	m_bRunStatus = false;

	m_uploadFileUrl = "";
	m_uploadFileSaveDir = "";
	m_uploadFileBack = nullptr;
	CGetHostByName::initMongoose();
}

CHttpService::~CHttpService()
{
	stop();
	mg_mgr_free(&m_sMgr);
}

// 启动HTTP服务器
bool CHttpService::start(string url, struct mg_tls_opts *opts)
{
	if(m_bRunStatus){
		return true;
	}
	if(!url.empty()){
		m_sUrl = url;
	}
	if(opts){
		memcpy(&m_sOpts, opts, sizeof(struct mg_tls_opts));
	}
	if(mg_http_listen(&m_sMgr,m_sUrl.c_str(), httpMsgFn, (void*)this) == NULL){
        qDebug("Create HTTP listener error");
		return false;
	}

    m_bRunStatus = true;
    QThread::start();
	return true;
}

// 停止HTTP服务器
void CHttpService::stop()
{
	if(!m_bRunStatus){
		return;
	}
	m_bRunStatus = false;
    QThread::wait();
}

// 保存连接信息
void CHttpService::saveConnection(struct mg_connection *c)
{
	if(!c) {
		return ;
	}

	m_mHttpConnectInfo[c->id] = c;
}

// 删除连接信息
void CHttpService::delConnection(struct mg_connection *c)
{
	if(!c) {
		return;
	}

	if(m_mHttpConnectInfo.find(c->id) == m_mHttpConnectInfo.end()){
		return;
	}

	LOCK_STATUS;
	m_mHttpConnectInfo.erase(c->id);
}


void CHttpService::__httpServiceCB(struct mg_connection *c, int ev, void *ev_data)
{
   if (ev == MG_EV_ACCEPT) {
	   if (mg_url_is_ssl(m_sUrl.c_str())) {
			 mg_tls_init(c, &m_sOpts);
	   }
	   saveConnection(c);
   } else if(ev == MG_EV_HTTP_MSG){
	   saveConnection(c);
	   struct mg_http_message *hm = (struct mg_http_message *)ev_data;
	   if(!runHandler(c, hm)) {
	   }
   }else if(ev == MG_EV_HTTP_CHUNK){
	   struct mg_http_message *hm = (struct mg_http_message *)ev_data;
	   if(chunkHandler(c, hm)){
			mg_http_delete_chunk(c, hm);
	   }
	}else if(ev == MG_EV_CLOSE_BEFORE){
	   delConnection(c);
   }
}

// 发送数据
bool CHttpService::sendData(unsigned long connectId, MAP_PARAMS head, string bodyData)
{
	LOCK_STATUS;
	if(m_mHttpConnectInfo.find(connectId) == m_mHttpConnectInfo.end()){
        qDebug("sendData error %ld",connectId);
		return false;
	}
	struct mg_connection *c = m_mHttpConnectInfo[connectId];
	if(c == NULL){
        qDebug("sendData error %ld",connectId);
		return false;
	}
	string heads;
	for(MAP_PARAMS::iterator itr = head.begin(); itr != head.end(); ++itr){
		heads += itr->first + ":" + itr->second + "\r\n";
	}
	mg_http_reply(c, 200, heads.c_str(), "%s", bodyData.c_str());
    return true;
}

// 发送数据
bool CHttpService::sendData(unsigned long connectId,string heads, string bodyData)
{
	LOCK_STATUS;
	if(m_mHttpConnectInfo.find(connectId) == m_mHttpConnectInfo.end()){
        qDebug("sendData error %ld",connectId);
		return false;
	}
	struct mg_connection *c = m_mHttpConnectInfo[connectId];
	if(c == NULL){
        qDebug("sendData error %ld",connectId);
		return false;
	}
    mg_http_reply(c, 200, heads.c_str(), "%s", bodyData.c_str());
    return true;
}

// 发送数据
bool CHttpService::sendData(unsigned long connectId, struct mg_http_serve_opts sopts, struct mg_http_message *hm)
{
	LOCK_STATUS;
	if(m_mHttpConnectInfo.find(connectId) == m_mHttpConnectInfo.end()){
        qDebug("sendData error %ld",connectId);
		return false;
	}
	struct mg_connection *c = m_mHttpConnectInfo[connectId];
	if(c == NULL){
        qDebug("sendData error %ld",connectId);
		return false;
	}
    mg_http_serve_dir(c, hm, &sopts);
    return true;
}

// 注册处理函数
bool CHttpService::registerHandler(string url,httpUrlhander back,void *data)
{
	if(m_mHttpHandler.find(url) != m_mHttpHandler.end()){
        qDebug("registerHandler error %s",url.c_str());
		return false;
	}
	struct urlHandler handler;
	handler.url = url;
	handler.data = data;
	handler.handler = back;
	m_mHttpHandler[url]=handler;
	return true;
}

// 注册处理函数
void CHttpService::unRegisterHandler(string url)
{
	if(m_mHttpHandler.find(url) != m_mHttpHandler.end()){
		m_mHttpHandler.erase(url);
	}
}

struct SubstrFinder {
    SubstrFinder(const std::string& substr) : m_substr(substr) {}

    bool operator()(const std::pair<const std::string, struct urlHandler>& kv) const {
    	return mg_globmatch(kv.first.c_str(), kv.first.size(), m_substr.c_str(), m_substr.size());
    }

    std::string m_substr;
};

// 回调处理函数
bool CHttpService::runHandler(struct mg_connection *c, struct mg_http_message *hm){

	bool run = false;
	bool ret = true;
	auto it = std::find_if(m_mHttpHandler.begin(), m_mHttpHandler.end(), SubstrFinder(string(hm->uri.ptr, hm->uri.len)));
	if(it != m_mHttpHandler.end()){

		run = true;
		struct urlHandler *handler = &it->second;
		if(handler->handler){
			ret = handler->handler(c,hm,handler->data);
		}
		return ret;

	}

    // 文件接收处理
	if ((!m_uploadFileUrl.empty() && !m_uploadFileSaveDir.empty()) && mg_http_match_uri(hm, m_uploadFileUrl.c_str())) {
		run = true;
		hm->chunk.ptr = hm->body.ptr;
		hm->chunk.len = hm->body.len;

		chunkHandler(c, hm);
		return true;
	}

	if(m_webBack){
		m_webBack(c, hm);
		return false;
	}
    qDebug("%.*s handler is NUll",hm->uri.len,hm->uri.ptr);
	mg_http_reply(c, 404, "", "%s", "Not found run handler\n");

    return ret;
}


static void zeromem(volatile unsigned char *buf, size_t len) {
  if (buf != NULL) {
    while (len--) *buf++ = 0;
  }
}

// 块数据处理函数
bool CHttpService::chunkHandler(struct mg_connection *c, struct mg_http_message *hm)
{
	if(m_uploadFileUrl.empty() || m_uploadFileSaveDir.empty()){
		return false;
	}
	if (!mg_http_match_uri(hm, m_uploadFileUrl.c_str())) {
		return false;
	}
	auto itr = m_recvFileMap.find(c->id);
	if(hm->chunk.len <= 0){
		if(itr == m_recvFileMap.end()){
			return true;
		}
		if(itr->second.fp){
            fflush(itr->second.fp);
			fclose(itr->second.fp);
			itr->second.fp = nullptr;

			// 说明接收完成，要清除接收IO，不然下一次请求会有问题
			struct mg_iobuf *io = &c->recv;
			zeromem(io->buf, io->len);
			io->len = 0;

			if(m_uploadFileBack)
				m_uploadFileBack(c,itr->second.saveFilePath);

			m_recvFileMap.erase(itr->first);
			return false;
		}
		return true;
	}

	if(itr == m_recvFileMap.end()){
		m_recvFileMap[c->id] = T_RecvFileInfo();
	}

	T_RecvFileInfo & infoRecv = m_recvFileMap[c->id];

	if(!infoRecv.fp){
		char offset[40] = {0}, name[200] = {0};
		mg_http_get_var(&hm->query, "offset", offset, sizeof(offset));
		mg_http_get_var(&hm->query, "name", name, sizeof(name));
		if (name[0] == '\0') {
			mg_http_reply(c, 400, "", "%s", "name required");
			return false;
        }
		size_t oft = strtoul(offset, NULL, 0);
		CUtils::createMultiLevelDir((char*)m_uploadFileSaveDir.c_str());
		infoRecv.saveFilePath = m_uploadFileSaveDir + CUtils::getOnlyId() + name;
        qDebug("m_saveFilePath = %s", infoRecv.saveFilePath.c_str());
		infoRecv.fp = fopen(infoRecv.saveFilePath.c_str(), oft == 0 ? "wb" : "ab");
		if(!infoRecv.fp){
            qDebug("fopen(%s): %d", infoRecv.saveFilePath.c_str(), errno);
			mg_http_reply(c, 400, "", "fopen(%s): %d", name, errno);
			m_recvFileMap.erase(c->id);
			return false;
		}
	}


	if(infoRecv.fileTotalSize == 0){
		for(int i = 0; i < MG_MAX_HTTP_HEADERS; i++){
			if(string(hm->headers[i].name.ptr, hm->headers[i].name.len) == "Content-Length"){
				// 要接收的文件总大小
				infoRecv.fileTotalSize = atoll(string(hm->headers[i].value.ptr, hm->headers[i].value.len).c_str());
				break;
			}
		}
		for(int i = 0; i < MG_MAX_HTTP_HEADERS; i++){
			if(string(hm->headers[i].name.ptr, hm->headers[i].name.len) == "Content-Type"){
				// 兼容文件上传方式
				infoRecv.contentType = string(hm->headers[i].value.ptr, hm->headers[i].value.len);
				string findValue = "boundary=";
				if(infoRecv.contentType.find(findValue) != infoRecv.contentType.npos){
					int startPos =  infoRecv.contentType.find(findValue) + findValue.length();
					infoRecv.contentType = infoRecv.contentType.substr(startPos);
					infoRecv.contentType = string("--") + infoRecv.contentType;

				}
				break;
			}
		}
	}

	if(hm->chunk.len <= 0){
        fflush(infoRecv.fp);
		fclose(infoRecv.fp);
		infoRecv.fp = nullptr;
	} else {
		infoRecv.fileCurrentSize += hm->chunk.len;
		struct mg_http_part part;
		size_t ofs = 0;
		if(infoRecv.contentType.empty()){
			fwrite(hm->chunk.ptr, 1, hm->chunk.len, infoRecv.fp);
		} else {
			string chunk = string(hm->chunk.ptr, hm->chunk.len);
			if(chunk.find(infoRecv.contentType) == chunk.npos){
				fwrite(hm->chunk.ptr, 1, hm->chunk.len, infoRecv.fp);
			} else {
				size_t pos = chunk.find(infoRecv.contentType);
				if(pos == 0){
					// 说明是开头
					pos = chunk.find("\r\n\r\n");
					pos +=4;
					fwrite(hm->chunk.ptr + pos, 1, hm->chunk.len - pos, infoRecv.fp);
				} else if(pos > 0){
					// 说明是结尾
					fwrite(hm->chunk.ptr, 1, pos-2, infoRecv.fp);
                    fflush(infoRecv.fp);
					fclose(infoRecv.fp);
					infoRecv.fp = nullptr;
				}
			}
		}
	}

	if(infoRecv.fileTotalSize > 0 && infoRecv.fileCurrentSize >= infoRecv.fileTotalSize){
		if(infoRecv.fp){
            fflush(infoRecv.fp);
			fclose(infoRecv.fp);
			infoRecv.fp = nullptr;
		}
	}
	if(!infoRecv.fp){
		// 说明接收完成，要清除接收IO，不然下一次请求会有问题
		struct mg_iobuf *io = &c->recv;
		zeromem(io->buf, io->len);
		io->len = 0;

		if(m_uploadFileBack)
			m_uploadFileBack(c,infoRecv.saveFilePath);
		infoRecv.saveFilePath = "";
		infoRecv.fileTotalSize = 0;
		infoRecv.fileCurrentSize = 0;
		infoRecv.contentType = "";
		m_recvFileMap.erase(c->id);
		return false;
	}
	return true;
}


// 运行线程
void CHttpService::run()
{

	while(m_bRunStatus){
		mg_mgr_poll(&m_sMgr, 100);
	}
}

BASENET_END_NAMESPACE
